import math
import random
import warnings

import Maps
import Shared
import Defines


class PointOfView:
    def __init__(self, pos=(0,0), angle=0):
        self.x = pos[0]
        self.y = pos[1]
        self.angle = angle

        
class Player(PointOfView):
    def __init__(self, pos=(0,0), angle=0, turn_speed=90, move_speed=768):
        PointOfView.__init__(self, pos, angle)
        self.angle_speed = turn_speed
        self.move_speed = move_speed
        self.xold = self.x
        self.yold = self.y
        self.health = 100
        
    # Walls can be crossed, gets worse with lower fps and if the player is running
    def validate_position(self):
        ''' Checks if the position is valid. If not, reverts it to old values and returns False. '''
        gridposold = Shared.togridF((self.xold, self.yold))
        gridpos = Shared.togridF((self.x, self.y))
                        
        if (not Maps.Maps.is_valid_grid((gridpos))) or Maps.Maps.wall_onG(gridpos):
            # Invalid position, reverting to previous
            self.x = self.xold
            self.y = self.yold
            return False
                
               
        return True # position was ok
    
    
    # Not working properly
    #def validate_position(self):
    #    ''' Checks if the position is valid. If not, reverts it to old values and returns False. '''
    #    gridposold = Shared.togridF((self.xold, self.yold))
    #    gridpos = Shared.togridF((self.x, self.y))
    #    
    #    angle_var = 2 #adjacent angle variation for collision detection
    #    
    #    # Gets the shortest ray and calculates the maximum grid variation in each direction
    #    x_rays = (Shared.cast_vertical_ray((self.x, self.y), self.angle),
    #                         Shared.cast_vertical_ray((self.x, self.y), self.angle-angle_var),
    #                         Shared.cast_vertical_ray((self.x, self.y), self.angle+angle_var))
    #    maximum_x_var = (min(x_rays)) - 1
    #    
    #    maximum_y_var = (min((Shared.cast_horizontal_ray((self.x, self.y), self.angle),
    #                         Shared.cast_horizontal_ray((self.x, self.y), self.angle-angle_var),
    #                         Shared.cast_horizontal_ray((self.x, self.y), self.angle+angle_var)))) - 1
    #    
    #    #gridvar = (abs(gridpos[0] - gridposold[0]), abs(gridpos[1] - gridposold[1]))
    #    gridvar = (gridpos[0] - gridposold[0], gridpos[1] - gridposold[1])
    #    absgridvar = (abs(gridvar[0]), abs(gridvar[1]))
    #    
    #    # If the player went past the maximum distance, put him in the maximum he could have walked
    #    if absgridvar[0] > maximum_x_var:
    #        print x_rays
    #        print maximum_x_var
    #        if gridvar[0] > 0:
    #            correctx = (gridposold[0] + maximum_x_var)
    #            self.x = int(correctx * Defines.CUBE_SIZE) + (Defines.CUBE_SIZE - 1)
    #        else:
    #            correctx = (gridposold[0] - maximum_x_var)
    #            self.x = int(correctx * Defines.CUBE_SIZE)
    #    
    #    if absgridvar[1] > maximum_y_var:
    #        if gridvar[1] > 0:
    #            correcty = (gridposold[1] + maximum_y_var)
    #            self.y = int(correcty * Defines.CUBE_SIZE) + (Defines.CUBE_SIZE - 1)
    #        else:
    #            correcty = (gridposold[1] - maximum_y_var)
    #            self.y = int(correcty * Defines.CUBE_SIZE)
    #                               
    #    return True # position was ok
    def player_on_sight(player):
        #TODO
        pass


    def alive(self):
        return self.health > 0

    def move_to_unchecked(self, angle, mult_factor):
        ''' Moves the player. '''
        self.xold = self.x
        self.yold = self.y
        self.x += self.move_speed * math.cos(math.radians(angle)) * mult_factor
        self.y -= self.move_speed * math.sin(math.radians(angle)) * mult_factor
        return True

             
    def move_to(self, angle, mult_factor):
        ''' Moves the player. '''
        self.xold = self.x
        self.yold = self.y
        self.x += self.move_speed * math.cos(math.radians(angle)) * mult_factor
        self.y -= self.move_speed * math.sin(math.radians(angle)) * mult_factor
        return self.validate_position()
                        

    def move_forward(self, mult_factor):
        ''' Moves the player forward. '''
        self.move_to(self.angle, mult_factor)
        
        
    def move_backward(self, mult_factor):
        ''' Moves the player backward. '''
        self.move_to(self.angle + 180, mult_factor)
        

    def turn_left(self, mult_factor):
        ''' Turns the angle of view to the left. '''
        self.angle += self.angle_speed * mult_factor
        

    def turn_right(self, mult_factor):
        ''' Turns the angle of view to the right. '''
        self.angle -= self.angle_speed * mult_factor
        

    def strafe_left(self, mult_factor):
        ''' Strafes the player to the left. '''
        self.move_to(self.angle + 90, mult_factor)
        

    def strafe_right(self, mult_factor):
        ''' Strafes the player to the right. '''
        self.move_to(self.angle - 90, mult_factor)


class Enemy(Player):
    def __init__(self, template='default', pos=(0,0), patrol_limits=None):
        '''
        Enemy:
        
        patrol_limits:
        - Area for patrolling. Tuple containing two grid positions(in unit coord) or None for static enemies.
        - The first grid point must be the top/left one in the range.
        
                
        '''

        Player.__init__(self, pos)
        self.load_template(template)

        self.state = 'patrol'
        
        assert patrol_limits[0][0] == patrol_limits[1][0] or patrol_limits[0][1] == patrol_limits[1][1]
        self.patrol_limits = patrol_limits
        self.patrol_direction_vertical = True if patrol_limits[0][0] == patrol_limits[1][0] else False
        self.patrol_sense = True #True == towards second grid point
        

    def load_template(self, template):
        self.template = template
        if 0:
            pass
        else:
            self.move_speed = 512
            self.turn_speed = 90
            self.damage = 10
            self.health = 50
            self.hit_chance = 80
            if not template in ('soldier', 'default'):
                self.template = 'default'
                print "Template -%s- not found. Loading default..." % template


    def main(self, player, mult_factor):
        if not self.alive(): # is dead
            return False
                
        if self.state == 'patrol':
            if self.patrol_limits: # does this enemy patrol an area?
                # Vertical
                if self.patrol_direction_vertical:
                    if self.patrol_sense:
                        moveok = self.move_to(270, mult_factor) #moving South
                    else:
                        moveok = self.move_to(90, mult_factor) #moving North
                
                # Horizontal
                else:
                    if self.patrol_sense:
                        moveok = self.move_to(0, mult_factor) #moving East
                    else:
                        moveok = self.move_to(180, mult_factor) #moving West

                # Time to change direction?
                x_in_range = self.patrol_limits[0][0] <= self.x <= self.patrol_limits[1][0]
                y_in_range = self.patrol_limits[0][1] <= self.y <= self.patrol_limits[1][1]
                if not moveok or not(x_in_range and y_in_range):
                    if not y_in_range:
                        self.y = self.patrol_limits[1][1] if self.y > self.patrol_limits[1][1] else self.patrol_limits[0][1]
                    if not x_in_range:
                        self.x = self.patrol_limits[1][0] if self.x > self.patrol_limits[1][0] else self.patrol_limits[0][0]
                                      
##                    print "inverting"
##                    print 'moveok:', moveok
##                    print 'first block:', self.patrol_limits[0]
##                    print '2nd block:', self.patrol_limits[1]
##                    print 'enemy x:', self.x
##                    print 'enemy y:', self.y
                    self.patrol_sense = not(self.patrol_sense) #invert patrol_sense

                
        elif self.state == 'search':
            pass
        

        elif self.state == 'fire':
            if random.randint(0, 99) < self.hit_chance:
                # hit
                player.health -= self.damage
                if not player.alive():
                    self.state = 'patrol' # killed player
            else:
                # missed
                pass

            if not self.player_on_sight(player):
                self.state == 'search'

        else:
            assert 0 # Impossible!

        return True

